package com.bytz.modules.cms.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bytz.common.exception.BytzBootException;
import com.bytz.common.system.base.service.impl.AbstractCrudService;
import com.bytz.common.system.vo.DictModel;
import com.bytz.common.util.PageUtils;
import com.bytz.common.util.SecurityUtils;
import com.bytz.common.util.mentity.EntityMergeInfoV2;
import com.bytz.common.util.mentity.util.MergeEntityOptions;
import com.bytz.common.util.mentity.util.MergeEntityUtil;
import com.bytz.modules.bpm.client.service.utils.FlowUtils;
import com.bytz.modules.cms.c4c.model.*;
import com.bytz.modules.cms.c4c.service.IC4cFeignService;
import com.bytz.modules.cms.c4c.sync.C4cOdataFeign;
import com.bytz.modules.cms.common.constants.AdminAuthConstants;
import com.bytz.modules.cms.common.model.CmsConfig;
import com.bytz.modules.cms.common.model.CmsSystemConfig;
import com.bytz.modules.cms.common.service.CmsConfigService;
import com.bytz.modules.cms.common.util.C4cUtil;
import com.bytz.modules.cms.common.util.ExcelUtil;
import com.bytz.modules.cms.domain.util.DecimalFormatUtil;
import com.bytz.modules.cms.domain.util.PriceComputerUtil;
import com.bytz.modules.cms.echop.constants.EChopConstants;
import com.bytz.modules.cms.order.constant.OrderConstants;
import com.bytz.modules.cms.order.entity.Order;
import com.bytz.modules.cms.order.entity.OrderItem;
import com.bytz.modules.cms.order.mapper.OrderItemMapper;
import com.bytz.modules.cms.order.mapper.OrderMapper;
import com.bytz.modules.cms.order.model.*;
import com.bytz.modules.cms.order.service.IAdminOrderService;
import com.bytz.modules.cms.order.service.IOrderItemService;
import com.bytz.modules.cms.order.util.OrderUtils;
import com.bytz.modules.cms.product.entity.Product;
import com.bytz.modules.cms.product.service.IProductService;
import com.bytz.modules.cms.productLine.entity.ProductLine;
import com.bytz.modules.cms.productLine.service.IProductLineService;
import com.bytz.modules.cms.reseller.entity.EndUser;
import com.bytz.modules.cms.reseller.entity.Reseller;
import com.bytz.modules.cms.reseller.service.IEndUserService;
import com.bytz.modules.cms.reseller.service.IResellerService;
import com.bytz.modules.cms.user.constants.CmsSysUserConstants;
import com.bytz.modules.cms.user.entity.CmsSysUser;
import com.bytz.modules.cms.user.entity.EcAudit;
import com.bytz.modules.cms.user.service.ICmsSysUserService;
import com.bytz.modules.cms.user.service.IEcAuditService;
import com.bytz.modules.system.entity.SysUser;
import com.bytz.modules.system.service.ISysDictService;
import com.bytz.modules.system.service.ISysUserRoleService;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AdminOrderServiceImpl extends AbstractCrudService<OrderMapper, Order, AdminOrderModel> implements IAdminOrderService {

    @Autowired
    private IOrderItemService itemService;

    @Autowired
    private IProductService productService;

    @Autowired
    private IProductLineService productLineService;

    @Autowired
    private IResellerService resellerService;

    @Resource
    private OrderItemMapper itemMapper;

    @Autowired
    private IEcAuditService ecAuditService;

    @Autowired
    private IEndUserService endUserService;

    @Autowired
    private IC4cFeignService c4cService;

    @Autowired
    private ICmsSysUserService sysUserService;

    @Autowired
    private ISysDictService dictService;

    @Autowired
    private CmsConfigService configService;

    @Override
    protected AdminOrderModel insertSaveObject(AdminOrderModel saveRo) {
        save(saveRo);
        return saveRo;
    }

    @Override
    protected AdminOrderModel updateSaveObject(AdminOrderModel saveRo) {
        updateById(saveRo);
        return saveRo;
    }

    @Override
    protected AdminOrderModel saveOthers(AdminOrderModel saveRo) {
        return null;
    }

    @Override
    protected boolean deleteEntity(Order entity) {
        return removeById(entity.getId());
    }

    @Override
    protected void deleteOthers(Order entity) {

    }

    @Override
    public AdminOrderModel queryById(String id, boolean hasCostPriceView) {
        // 根据系统参数获取税率
        BigDecimal taxRate = configService.getGlobal().getTaxRate();
        AdminOrderModel orderModel = getOrderModel(id, hasCostPriceView);
        Optional<ProductLine> loginUserLine = productLineService.getLoginUserLine();
        List<OrderItemModel> orderItemModels = orderModel.getItem();
        orderItemModels.forEach(itemModel -> {
            // 计算协议价不含税
            if (itemModel.getNegotiatePrice() == null) {
                itemModel.setNoTaxNegotiatePrice(BigDecimal.ZERO);
            }else {
                BigDecimal noTaxNegotiatePrice =  itemModel.getNegotiatePrice().divide(taxRate.add(BigDecimal.valueOf(1)), 2, RoundingMode.HALF_UP);
                itemModel.setNoTaxNegotiatePrice(noTaxNegotiatePrice);
            }

            if (loginUserLine.isPresent()) {
                itemModel.setCanViewDetail(StringUtils.equals(loginUserLine.get().getProductLineCode(), itemModel.getProductLineCode()));
            } else {
                itemModel.setCanViewDetail(true);
            }
        });
        if (OrderUtils.ecRequired(orderModel)) {
            Optional<EcAudit> ecAudit = ecAuditService.queryByOrderId(id);
            ecAudit.ifPresent(ec -> orderModel.setEcAuditProcessId(ec.getProcInstId()));
        }

        return orderModel;
    }


    public AdminOrderModel getOrderModel(String id, boolean hasCostPriceView) {

        Order order = getById(id);

        if (order == null) {
            throw new BytzBootException("EC对应订单不存在，orderId:" + id);
        }

        AdminOrderModel model = BeanUtil.copyProperties(order, AdminOrderModel.class);

        List<OrderItemModel> orderItemModels = BeanUtil.copyToList(itemService.getByOrderId(id), OrderItemModel.class);
        model.setItem(orderItemModels);

        if (hasCostPriceView) {
            addCostPriceAndMargin(model, orderItemModels);
        }
        return model;
    }

    /**
     * 计算订单毛利率、填入成本价
     *
     * @param model           订单model
     * @param orderItemModels 订单itemModel
     */
    private void addCostPriceAndMargin(AdminOrderModel model, List<OrderItemModel> orderItemModels) {

        Set<String> productNumbers = orderItemModels.stream().map(OrderItem::getProductNumber).collect(Collectors.toSet());

        Map<String, Product> numberProductMap = productService.getByProductNumbers(productNumbers)
                .stream().collect(Collectors.toMap(Product::getProductNumber, t -> t));

        // 不含税协议总价
        BigDecimal negotiatePriceCount = BigDecimal.ZERO;
        // 成本价总和
        BigDecimal costPriceCount = BigDecimal.ZERO;

        for (OrderItemModel itemModel : orderItemModels) {
            // StF Margin = (不含税协议单价-seed cost)/不含税协议单价
            Product product = numberProductMap.get(itemModel.getProductNumber());
            if (product == null || product.getCostPrice() == null) {
                continue;
            }

            itemModel.setCostPrice(product.getCostPrice());

            // 单个产品经销商付款金额(含税协议价)
            BigDecimal singleItemUnPaidTax = PriceComputerUtil.divide(itemModel.getTotalTaxPrice(), BigDecimal.valueOf(itemModel.getProductNum()));
            // 单个产品莱宝入账金额(税后)
            BigDecimal singleItemPaidTax = PriceComputerUtil.divide(singleItemUnPaidTax, BigDecimal.ONE.add(model.getTaxRate()));
            // 单个产品盈利
            BigDecimal subtract = singleItemPaidTax.subtract(product.getCostPrice());

            itemModel.setStfMargin(PriceComputerUtil.divide(subtract, singleItemPaidTax));

            // 计算总数，算订单毛利率用
            BigDecimal itemNegotiatePriceCount = PriceComputerUtil.divide(itemModel.getTotalTaxPrice(), BigDecimal.ONE.add(model.getTaxRate()));
            negotiatePriceCount = negotiatePriceCount.add(itemNegotiatePriceCount);

            BigDecimal itemCostPriceCount = itemModel.getCostPrice().multiply(BigDecimal.valueOf(itemModel.getProductNum()));
            costPriceCount = costPriceCount.add(itemCostPriceCount);
        }

        // 订单毛利率
        model.setStfMargin(PriceComputerUtil.divide(negotiatePriceCount.subtract(costPriceCount), negotiatePriceCount));
    }

    @Override
    public IPage<AdminOrderModel> queryPage(Page<Order> page, QueryWrapper<Order> initQueryWrapper) {
        // 草稿订单对审批没有意义，筛掉
        initQueryWrapper.lambda().ne(Order::getStatus, OrderConstants.OrderStatus.CAO_GAO);
        constructWrapper(initQueryWrapper);

        Set<String> canTerminateSet = Sets.newHashSet(
                OrderConstants.OrderStatus.SHEN_HE_TONG_GUO,
                OrderConstants.OrderStatus.DAI_GAI_ZHANG,
                OrderConstants.OrderStatus.DAI_SHANG_CHUAN_HE_TONG,
                OrderConstants.OrderStatus.DAI_YU_FU,
                OrderConstants.OrderStatus.YI_YU_FU,
                OrderConstants.OrderStatus.PAI_CHAN_ZHONG,
                OrderConstants.OrderStatus.DAI_TI_JIAO_WEI_KUAN
        );
        boolean hasRoleTerminate = SecurityUtils.hasAuthority(AdminAuthConstants.Order.TERMINATE);

        Page<Order> resultPage = page(page, initQueryWrapper);
        return PageUtils.convertPage(resultPage, AdminOrderModel.class, (out, in) -> {
            out.setCanAudit(FlowUtils.calculateCanAudit(in));
            out.setCanUploadContract(OrderConstants.OrderStatus.DAI_GAI_ZHANG.equals(in.getStatus()) || OrderConstants.OrderStatus.SHEN_HE_TONG_GUO.equals(in.getStatus()));
            out.setCanDownloadContract((in.getEChopDocumentId() != null || in.getContractSignedFile() != null) &&
                    !OrderConstants.OrderStatus.DAI_GAI_ZHANG.equals(in.getStatus()));
            out.setCanTerminate(hasRoleTerminate && canTerminateSet.contains(out.getStatus()));
        });
    }

    private void constructWrapper(QueryWrapper<Order> initQueryWrapper){
        // 销售只能看到自己的订单，大区经理只能看到自己区域销售的订单
        if (!SecurityUtils.hasAuthority(AdminAuthConstants.Order.VIEW_ALL)) {
            boolean isRegionManager = SecurityUtils.hasRole(CmsSysUserConstants.RoleCode.REGION_SALES_MANAGER);
            boolean isOrderManager = SecurityUtils.hasRole(CmsSysUserConstants.RoleCode.ORDER_MANAGER);
            initQueryWrapper.lambda().and(wrapper -> {
                wrapper.eq(Order::getSalesId, SecurityUtils.getLoginUser().getId());
                if (isRegionManager) {
                    List<String> salesIds = sysUserService.getSalesIdsByRegionManager(SecurityUtils.getLoginUser().getId());
                    if (CollectionUtil.isNotEmpty(salesIds)) {
                        wrapper.or().in(Order::getSalesId, salesIds);
                    }
                }
                if (isOrderManager) {
                    Set<String> salesIds = sysUserService.getSalesIdsByOM(SecurityUtils.getLoginUser().getId());
                    if (CollectionUtil.isNotEmpty(salesIds)) {
                        wrapper.or().in(Order::getSalesId, salesIds);
                    }
                }
            });
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean importHistory(List<OrderExcel> models, List<OrderItemExcel> items) {
        Map<String, Field> fieldMap = ReflectUtil.getFieldMap(OrderExcel.class);
        List<Field> modelTypes = fieldMap.values().stream().filter(f -> f.getType() == BigDecimal.class).collect(Collectors.toList());
        models.forEach(m->
                modelTypes.forEach(field->{
                    try {
                        field.setAccessible(true);
                        BigDecimal value = (BigDecimal)field.get(m);
                        BigDecimal newValue = value.setScale(2, RoundingMode.HALF_UP);
                        field.set(m, newValue);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                })
        );


        Map<String, Field> fieldItemMap = ReflectUtil.getFieldMap(OrderItemExcel.class);
        List<Field> itemTypes = fieldItemMap.values().stream().filter(f -> f.getType() == BigDecimal.class).collect(Collectors.toList());
        items.forEach(m->
                itemTypes.forEach(field->{
                    try {
                        field.setAccessible(true);
                        BigDecimal value = (BigDecimal)field.get(m);
                        BigDecimal newValue = value.setScale(2, RoundingMode.HALF_UP);
                        field.set(m, newValue);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                })
        );

        Map<String, List<OrderItemExcel>> itemMap = items.stream().collect(Collectors.groupingBy(OrderItemExcel::getContractNumber));

        // 获取所有reseller
        Map<String, Reseller> resellerMap = resellerService.getByNames(models.stream().map(orderExcel -> orderExcel.getResellerName().replaceAll(" ", "")).collect(Collectors.toSet()))
                .stream().collect(Collectors.toMap(Reseller::getResellerName, t -> t));

        // 获取所有 end user 表数据（可能要分着查防止in不够）
        Set<String> endUserNumbers = models.stream().map(excel ->
                        new String[]{excel.getDemanderNumber(), excel.getShipToNumber(), excel.getEndUserNumber()})
                .flatMap(Arrays::stream).collect(Collectors.toSet());
        Map<String, EndUser> endUserMap = endUserService.findByCustomerNumber(endUserNumbers)
                .stream().collect(Collectors.toMap(EndUser::getCustomerNumber, t -> t));

        // 获取所有销售
        Map<String, CmsSysUser> salesMap = sysUserService.getByRealNames(models.stream().map(OrderExcel::getSalesName).collect(Collectors.toSet()))
                .stream().filter(item -> item.getDelFlag() == 0).collect(Collectors.toMap(SysUser::getRealname, t -> t));

        // 系统参数
        CmsConfig global = configService.getGlobal();

        List<Order> orders = models.stream().map(orderExcel -> {

            Reseller reseller = resellerMap.get(orderExcel.getResellerName().replaceAll(" ", ""));
            if (reseller == null) {
                throw new BytzBootException("找不到经销商：" + orderExcel.getResellerName());
            }
            EndUser soldTo = endUserMap.get(orderExcel.getDemanderNumber());
            if (soldTo == null) {
                throw new BytzBootException("找不到soldTo：" + orderExcel.getDemanderNumber());
            }
            EndUser shipTo = endUserMap.get(orderExcel.getShipToNumber());
            if (shipTo == null) {
                throw new BytzBootException("找不到shipTo：" + orderExcel.getShipToNumber());
            }
            EndUser endUser = endUserMap.get(orderExcel.getEndUserNumber());
            if (endUser == null) {
                throw new BytzBootException("找不到endUser：" + orderExcel.getEndUserNumber());
            }
            List<OrderItemExcel> itemExcels = itemMap.get(orderExcel.getContractNumber());
            if (CollectionUtil.isEmpty(itemExcels)) {
                throw new BytzBootException("没有找到订单对应的item：" + orderExcel.getContractNumber());
            }
            CmsSysUser sale = salesMap.get(orderExcel.getSalesName());
            if (BeanUtil.isEmpty(sale)) {
                throw new BytzBootException("没有销售：" + orderExcel.getSalesName());
            }
            if (OrderConstants.OrderStatus.DAI_YU_FU.equals(orderExcel.getStatus())) {
                if (orderExcel.getPaidPrice() != null && orderExcel.getPaidPrice().compareTo(BigDecimal.ZERO) != 0) {
                    throw new BytzBootException("待预付订单不能有已支付金额：" + orderExcel.getContractNumber());
                }
                if (itemExcels.stream().anyMatch(item -> item.getShippedNum() != null && item.getShippedNum() != 0)) {
                    throw new BytzBootException("待预付订单不能有已发货数量：" + orderExcel.getContractNumber());
                }
            }
            BigDecimal totalPrice = itemExcels.stream().map(OrderItemExcel::getTotalTaxPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            if (Math.abs(totalPrice.subtract(orderExcel.getTotalPrice()).doubleValue()) >= 0.01) {
                throw new BytzBootException("订单价格不等于item价格的和：" + orderExcel.getContractNumber());
            }

            BigDecimal paidPrice = itemExcels.stream().map(OrderItemExcel::getPaidPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            if (Math.abs(paidPrice.subtract(orderExcel.getPaidPrice()).doubleValue()) >= 0.01) {
                throw new BytzBootException("订单已支付价"+orderExcel.getPaidPrice()+"格不等于item已支付价格的和"+paidPrice+"：" + orderExcel.getContractNumber());
            }

            Order order = BeanUtil.copyProperties(orderExcel, Order.class);

            order.setSalesId(sale.getId());
            order.setSalesExternalId(sale.getBusinessPartnerId());
            order.setResellerId(reseller.getId());

            order.setDemanderId(soldTo.getId());
            order.setDemanderName(soldTo.getCustomerName());
            order.setDemanderAddress(soldTo.getAddress());
            order.setDemanderContact(soldTo.getContactPerson());
            order.setDemanderAgent(ObjectUtil.defaultIfNull(order.getDemanderAgent(), soldTo.getContactPerson()));
            order.setDemanderPhone(soldTo.getPhone());
            order.setDemanderFax(ObjectUtil.defaultIfNull(order.getDemanderFax(), soldTo.getFax()));
            order.setDemanderPostcode(ObjectUtil.defaultIfNull(order.getDemanderPostcode(), soldTo.getPostcode()));

            order.setShipToId(shipTo.getId());
            order.setShipToName(ObjectUtil.defaultIfNull(order.getShipToName(), shipTo.getCustomerName()));
            order.setShipToAddress(ObjectUtil.defaultIfNull(order.getShipToAddress(), shipTo.getAddress()));
            order.setShipToContact(ObjectUtil.defaultIfNull(order.getShipToContact(), shipTo.getContactPerson()));
            order.setShipToPhone(ObjectUtil.defaultIfNull(order.getShipToPhone(), shipTo.getPhone()));

            order.setEndUserId(endUser.getId());
            order.setEndUserName(endUser.getCustomerName());
            order.setEndUserAddress(endUser.getAddress());
            order.setEndUserContact(endUser.getContactPerson());
            order.setEndUserPhone(endUser.getPhone());

            order.setCompleteTime(order.getDemanderSignedDate());
            order.setSubmitTime(order.getDemanderSignedDate());

            order.setSubmitPrepayRatio(order.getPrepayRatio());

            order.setTaxRate(global.getTaxRate());
            order.setSupplierName(global.getSupplierUnitName());
            order.setSupplierAddress(global.getSupplierUnitAddress());
            order.setSupplierPhone(global.getSupplierPhone());
            order.setSupplierFax(global.getSupplierFax());
            order.setSupplierPostcode(global.getSupplierPostcode());
            order.setSignedPlace(global.getSignedPlace());
            order.setCurrency(global.getCurrency());
            order.setWarrantyPeriod(ObjectUtil.defaultIfNull(order.getWarrantyPeriod(), global.getDefaultWarrantyPeriod()));
            order.setPayType(ObjectUtil.defaultIfNull(order.getPayType(), global.getDefaultPayType()));
            order.setCreateTime(LocalDate.of(2023, 7, 31).atStartOfDay());
            return order;
        }).collect(Collectors.toList());

        EntityMergeInfoV2<Order> mergeInfo = MergeEntityUtil.calculateEntityMergeInfo(
                orders,
                list(),
                MergeEntityOptions.<Order>builder().keyExtractor(Order::getContractNumber).build()
        );
        saveBatch(mergeInfo.getAddEntities());
        updateBatchById(mergeInfo.getUpdateEntities());

        items.forEach(item -> {
            item.setShippedNum(item.getShippedNum() == null ? 0 : item.getShippedNum());
        });

        for (Order order : orders) {
            List<OrderItemExcel> itemExcels = itemMap.get(order.getContractNumber());
            List<OrderItem> orderItems = BeanUtil.copyToList(itemExcels, OrderItem.class);

            Map<String, Product> productMap = productService.getByProductNumbers(itemExcels.stream().map(OrderItemExcel::getProductNumber).collect(Collectors.toSet()))
                    .stream().collect(Collectors.toMap(Product::getProductNumber, t -> t));

            /*
              这里的逻辑有点复杂，简述一下
              首先根据订单排序，目的是将已付款的金额优先分配给已经全部提货的item
              第一步分配结束后，剩下的订单按照剩余尾款和订单总额的比例分配
              第一步不够的，属于信用发货订单，现在暂不支持，报错
             */

            orderItems.sort((first, second) -> {
                Integer firstRemainder = first.getProductNum() - first.getShippedNum();
                Integer secondRemainder = second.getProductNum() - second.getShippedNum();
                return firstRemainder.compareTo(secondRemainder);
            });

            // 订单的已付款金额剩余量
            BigDecimal remainderPrice = order.getPaidPrice();

            // 不包括已经全部提货的部分的总订单额度
            BigDecimal totalPriceExcludeAllPickup = orderItems.stream()
                    .map(item -> {
                        BigDecimal remainderRate = BigDecimal.valueOf(item.getShippedNum())
                                .divide(BigDecimal.valueOf(item.getProductNum()), 8, RoundingMode.HALF_UP);
                        return item.getTotalTaxPrice().multiply(remainderRate);
                    })
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            for (int i = 0; i < orderItems.size(); i++) {
                OrderItem orderItem = orderItems.get(i);

                if (OrderConstants.OrderType.STANDARD.equals(order.getOrderType())) {
                    // 标准订单清空协议价
                    orderItem.setNegotiatePrice(null);
                } else {
                    // 项目订单没有填写协议价，将含税价作为协议价
                    orderItem.setNegotiatePrice(ObjectUtil.defaultIfNull(orderItem.getNegotiatePrice(), orderItem.getIncludeTaxPrice()));
                }

                // 计算不含税价
                BigDecimal noTaxPrice = PriceComputerUtil.divide(orderItem.getIncludeTaxPrice(), BigDecimal.ONE.add(global.getTaxRate()));
                orderItem.setNoTaxPrice(noTaxPrice);

//                // 计算item已付价格
//                if (OrderConstants.OrderStatus.DAI_TI_JIAO_WEI_KUAN.equals(order.getStatus())) {
//                    if (orderItem.getProductNum() - orderItem.getShippedNum() == 0) {
//                        // 全部提货的情况，优先分配全额
//                        orderItem.setPaidPrice(orderItem.getTotalTaxPrice());
//                        BigDecimal remainder = remainderPrice.subtract(orderItem.getTotalTaxPrice());
//                        if (remainder.compareTo(BigDecimal.ZERO) < 0) {
//                            throw new BytzBootException("订单已支付金额不足以支付已全部提货的item：" + order.getContractNumber());
//                        }
//                        remainderPrice = remainder;
//                    } else {
//                        if (totalPriceExcludeAllPickup.compareTo(BigDecimal.ZERO) != 0) {
//                            // 不是最后一条，计算一下占总订单价格的比例，分配以下付款额度
//                            BigDecimal percentageOfTotalAmount = orderItem.getTotalTaxPrice().divide(totalPriceExcludeAllPickup, 8, RoundingMode.HALF_UP);
//                            BigDecimal itemPaidPrice = order.getPaidPrice().multiply(percentageOfTotalAmount).setScale(2, RoundingMode.HALF_UP);
//                            orderItem.setPaidPrice(itemPaidPrice);
//                            remainderPrice = remainderPrice.subtract(itemPaidPrice);
//                        } else {
//                            orderItem.setPaidPrice(BigDecimal.ZERO);
//                        }
//                    }
//                } else {
//                    orderItem.setShippedNum(0);
//                }

                orderItem.setOrderId(order.getId());

                Product product = productMap.get(orderItem.getProductNumber());
                if (product == null) {
                    throw new BytzBootException("找不到产品：" + orderItem.getProductNumber());
                }

                orderItem.setProductId(product.getId());
                orderItem.setProductDesc(product.getMaterialDesc());
                orderItem.setProductLineCode(product.getProductLineCode());
            }

            EntityMergeInfoV2<OrderItem> itemMergeInfo = MergeEntityUtil.calculateEntityMergeInfo(
                    orderItems,
                    itemService.getByOrderId(order.getId()),
                    MergeEntityOptions.<OrderItem>builder().build()
            );
            MergeEntityUtil.doDbMerge(itemMergeInfo, itemService);
        }

        return true;
    }

    @Override
    public void export(QueryWrapper<Order> initQueryWrapper, HttpServletResponse response) throws IOException {
        constructWrapper(initQueryWrapper);
        List<Order> orderList = list(initQueryWrapper);
        List<OrderExcel> excelList = BeanUtil.copyToList(orderList, OrderExcel.class);
        Set<String> resellerIds = orderList.stream().map(Order::getResellerId).collect(Collectors.toSet());
        if (CollectionUtil.isNotEmpty(resellerIds)) {
            Map<String, String> resellerMap = resellerService.lambdaQuery()
                    .in(Reseller::getId, resellerIds)
                    .select(Reseller::getId, Reseller::getResellerName)
                    .list().stream().collect(Collectors.toMap(Reseller::getId, Reseller::getResellerName));
        excelList.forEach(orderExcel -> orderExcel.setResellerName(resellerMap.get(orderExcel.getResellerId())));
        }

        ExcelUtil.export("导出订单", OrderExcel.class, response, excelList);
    }

    @Override
    public void uploadContract(Order order) {
        CmsSystemConfig systemConfig = configService.getSystemConfig();
        boolean update = lambdaUpdate()
                .eq(Order::getId, order.getId())
                .in(Order::getStatus, OrderConstants.OrderStatus.DAI_GAI_ZHANG, OrderConstants.OrderStatus.SHEN_HE_TONG_GUO )
                .set(Order::getStatus, OrderConstants.OrderStatus.DAI_SHANG_CHUAN_HE_TONG)
                .set(Order::getPayExpireTime, LocalDateTime.now().plusDays(systemConfig.getOrderPrepayTimeLimit()))
                .set(Order::getContractSignedFile, order.getContractSignedFile())
                .update();

        if (!update) {
            throw new BytzBootException("该订单无法上传合同");
        }
    }

    @Override
    public OrderItemAuditAssist productHistory(String orderId, String productNumber, String orderType) {

        OrderItemAuditAssist result = new OrderItemAuditAssist();

        Order order = getById(orderId);

        if (StringUtils.isNotBlank(order.getCopyFromOrderId())) {

            List<BigDecimal> copyFromOrderPaidPrice = itemService.getByOrderIdAndProductNumber(order.getCopyFromOrderId(), productNumber)
                    .stream().map(OrderItem::getPaidPrice).collect(Collectors.toList());

            result.setCopyFromOrderPrice(copyFromOrderPaidPrice);
        }

        // 经销商历史
        Page<HistoricalPrice> resellerHistorical = itemMapper.getResellerHistorical(
                order.getResellerId(),
                productNumber,
                OrderConstants.OrderStatus.YI_WAN_CHENG,
                orderType,
                new Page<>(1, 10)
        );
        result.setResellerHistory(resellerHistorical.getRecords());

        // 经销商等级历史
        Page<HistoricalPrice> sameLevelHistoricalByReseller = itemMapper.getSameLevelHistoricalByReseller(
                order.getResellerId(),
                productNumber,
                OrderConstants.OrderStatus.YI_WAN_CHENG,
                orderType,
                new Page<>(1, 10)
        );

        result.setRankHistory(new ArrayList<>());
        result.getRankHistory().addAll(sameLevelHistoricalByReseller.getRecords());

        if (sameLevelHistoricalByReseller.getTotal() < 10) {
            Page<HistoricalPrice> productHistory = itemMapper.getProductHistory(
                    order.getResellerId(),
                    productNumber,
                    OrderConstants.OrderStatus.YI_WAN_CHENG,
                    orderType,
                    new Page<>(1, 10 - sameLevelHistoricalByReseller.getTotal())
            );
            result.getRankHistory().addAll(productHistory.getRecords());
        }


        return result;
    }

    @Override
    public List<Order> getNoProcessPrepaidOrders() {
        return lambdaQuery()
                .in(Order::getStatus, Lists.newArrayList(
                        OrderConstants.OrderStatus.YI_YU_FU,
                        OrderConstants.OrderStatus.PAI_CHAN_ZHONG,
                        OrderConstants.OrderStatus.DAI_TI_JIAO_WEI_KUAN,
                        OrderConstants.OrderStatus.YI_WAN_CHENG))
                .isNull(Order::getOpportunityId)
                .isNull(Order::getProcInstId)
                .list();
    }

    @Override
    public List<Order> getSalesQuoteOrders() {
        return lambdaQuery()
                .in(Order::getStatus, Lists.newArrayList(
                        OrderConstants.OrderStatus.YI_YU_FU,
                        OrderConstants.OrderStatus.PAI_CHAN_ZHONG,
                        OrderConstants.OrderStatus.DAI_TI_JIAO_WEI_KUAN,
                        OrderConstants.OrderStatus.YI_WAN_CHENG))
                .isNull(Order::getQuotationId)
                .ge(Order::getCreateTime, LocalDate.of(2023, 8, 1).atStartOfDay())
                .list();
    }

    @Override
    public void createOpportunity(Order order) {
        EndUser demander = endUserService.getById(order.getDemanderId());
        List<OrderItem> items = itemService.getByOrderId(order.getId());
        // 不含税价格
        BigDecimal NoTaxPrice = PriceComputerUtil.divide(
                order.getPaidPrice().subtract(ObjectUtil.defaultIfNull(order.getDeductionPrice(), BigDecimal.ZERO)),
                BigDecimal.ONE.add(order.getTaxRate())
        );
        // 产品线
        Map<String, String> productLineMap = dictService.queryDictItemsByCode("c4c_product_line_code")
                .stream().collect(Collectors.toMap(DictModel::getText, DictModel::getValue));
        String businessLine = items.stream().map(item -> productLineMap.get(item.getProductLineCode()))
                .collect(Collectors.joining(","));
        // 最大期望交期
        LocalDateTime maxDeliveryDate = items.stream()
                .map(orderItem -> orderItem.getExpectedDeliveryDate() != null ?
                        orderItem.getExpectedDeliveryDate() :
                        order.getDemanderSignedDate().plusDays(orderItem.getDeliveryDate()))
                .max(LocalDateTime::compareTo)
                .orElseThrow(() -> new BytzBootException("未知订单交期"));
        // order转Opportunity的具体参数对应
        C4cOpportunity opportunity = new C4cOpportunity();
        opportunity.setProcessingTypeCode("OPPT");
        opportunity.setName(C4cUtil.getOpportunityName(order));
        opportunity.setProspectPartyID(demander.getAccountId());
        opportunity.setPrimaryContactPartyID(demander.getContactPersonId());
        opportunity.setOriginTypeCode("Z25");
        opportunity.setLifeCycleStatusCode("1");
        opportunity.setSalesCycleCode("001");
        opportunity.setSalesCyclePhaseCode("002");
        opportunity.setProbabilityPercent("100.000000");
        opportunity.setExpectedRevenueAmount(DecimalFormatUtil.formatForC4c(NoTaxPrice));
        opportunity.setExpectedRevenueAmountCurrencyCode("CNY");
        opportunity.setExpectedProcessingStartDate(toC4cTimeStamp(order.getDemanderSignedDate()));
        opportunity.setExpectedProcessingEndDate(toC4cTimeStamp(order.getDemanderSignedDate().plusMonths(6)));
        opportunity.setSalesForecastCategoryCode("1");
        opportunity.setSalesUnitPartyID("3500");
        opportunity.setSalesOrganisationID("3500");
        opportunity.setDistributionChannelCode("01");
        opportunity.setDivisionCode("L1");
        opportunity.setMainEmployeeResponsiblePartyID(sysUserService.getOMBySaleIds(order.getSalesId()).getBusinessPartnerId());
        opportunity.setSellerPartyID("3500");
        opportunity.setPhaseProgressEvaluationStatusCode("1");
        opportunity.setBusinessLine4(businessLine);
        opportunity.setExpectedShipmentDateKUT(toC4cTimeStamp(maxDeliveryDate));

        C4cOpportunity resultOpportunity = c4cService.createOpportunity(opportunity);
        lambdaUpdate()
                .eq(Order::getId, order.getId())
                .set(Order::getOpportunityId, resultOpportunity.getId())
                .set(Order::getOpportunityName, resultOpportunity.getName())
                .set(Order::getOpportunityObjectId, resultOpportunity.getObjectId())
                .update();
    }


    public String toC4cTimeStamp(LocalDateTime dateTime) {
        return "/Date(" + dateTime.toInstant(ZoneOffset.UTC).toEpochMilli() + ")/";
    }

    @Override
    public List<Order> getSystemStatusSyncList() {
        return lambdaQuery()
                .select(Order::getId,
                        Order::getResellerId,
                        Order::getSalesId,
                        Order::getContractNumber,
                        Order::getDemanderName,
                        Order::getSalesName,
                        Order::getC4cSalesOrderId,
                        Order::getSapSalesOrderId,
                        Order::getOpportunityId,
                        Order::getOpportunityObjectId,
                        Order::getQuotationId,
                        Order::getSapQuotationId,
                        Order::getStatus)
                .isNotNull(Order::getQuotationId)
                .and(qw -> qw
                        .isNull(Order::getSapSalesOrderId).or()
                        .isNull(Order::getSapQuotationId).or()
                        .isNull(Order::getC4cSalesOrderId))
                .list();
    }

    @Override
    public List<C4cOpportunity> getOpportunities(String id) {
        Order order = getById(id);
//        String accountId = endUserService.getAccountIdByCustomNumber(order.getDemanderNumber());
        EndUser endUser = Optional.ofNullable(endUserService.getById(order.getDemanderId())).orElseThrow(() -> new BytzBootException("未找到该订单关联的sold to"));
        CmsSysUser sysUser = Optional.ofNullable(sysUserService.getById(order.getSalesId())).orElseThrow(() -> new BytzBootException("未找到该订单对应的销售"));
        List<C4cOpportunity> c4cOpportunityList = c4cService.getOpportunityBySalesAndSoldTo(sysUser.getBusinessPartnerId(), endUser.getAccountId());
        Set<String> opportunityIdList = c4cOpportunityList.stream().map(C4cOpportunity::getId).collect(Collectors.toSet());
        if (CollectionUtil.isNotEmpty(opportunityIdList)) {
            // 查询销售对应的项目订单有没有选择过这几个机会（opportunityIds 系统中选择过的）
            List<String> opportunityIds = lambdaQuery()
                    .select(Order::getOpportunityId)
                    .in(Order::getOpportunityId, opportunityIdList)
                    .eq(Order::getSalesId, order.getSalesId())
                    .eq(Order::getOrderType, OrderConstants.OrderType.PROJECT)
                    .ne(Order::getStatus, OrderConstants.OrderStatus.YI_ZHONG_ZHI)
                    .list().stream().map(Order::getOpportunityId).collect(Collectors.toList());
            // 排除选择过的
            if (CollectionUtil.isNotEmpty(opportunityIds)) {
                c4cOpportunityList = c4cOpportunityList.stream()
                        .filter(opportunity -> !opportunityIds.contains(opportunity.getId()))
                        .collect(Collectors.toList());
            }
        }
        return c4cOpportunityList;
    }

    @Override
    public Boolean bindOpportunity(Order order) {
        return lambdaUpdate()
                .eq(Order::getId, order.getId())
                .set(Order::getOpportunityId, order.getOpportunityId())
                .set(Order::getOpportunityName, order.getOpportunityName())
                .set(Order::getOpportunityObjectId, order.getOpportunityObjectId())
                .update();
    }

    @Override
    public Boolean setEChopSignComplete(String id, String documentId, String status) {
        log.info("=============e-chop回调开始orderId={}, documentId={}, status={}", id, documentId, status);
        // e-chop回调的documentId为合同文档id，审批附件文档id用逗号隔开的字符串
        // 当前db中保存的是附件文档的id，替换为另一个合同文档的id保存
        Order order = getById(id);
        String contractDocumentId = StrUtil.split(documentId, ",").stream()
                .filter(document -> order.getEChopDocumentId() == null || !order.getEChopDocumentId().toString().equals(document))
                .findFirst().orElse(null);

        String orderStatus = EChopConstants.Status.COMPLETE.equals(status) ?
                OrderConstants.OrderStatus.DAI_SHANG_CHUAN_HE_TONG : OrderConstants.OrderStatus.YI_ZHONG_ZHI;

        CmsSystemConfig systemConfig = configService.getSystemConfig();

        // 更新状态，保存documentId，如果订单状态不为待盖章，说明已经手动上传了合同，不更新状态
        boolean update = lambdaUpdate()
                .eq(Order::getId, id)
                .set(OrderConstants.OrderStatus.DAI_GAI_ZHANG.equals(order.getStatus()), Order::getStatus, orderStatus)
                .set(Order::getEChopDocumentId, contractDocumentId)
                .set(Order::getPayExpireTime, LocalDateTime.now().plusDays(systemConfig.getOrderPrepayTimeLimit()))
                .update();
        log.info("=============e-chop回调结束orderId={}", id);
        return update;
    }

    @Override
    public Boolean terminate(String id) {
        return lambdaUpdate()
                .eq(Order::getId, id)
                .set(Order::getStatus, OrderConstants.OrderStatus.YI_ZHONG_ZHI)
                .update();
    }

}